package com.uwlaser.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.uwlaser.domain.*;
import com.uwlaser.service.DataPassStationService;
import com.uwlaser.service.InboundService;
import com.uwlaser.service.dto.InboundBatchParam;
import com.uwlaser.service.dto.InboundBatchResult;
import com.uwlaser.service.dto.InboundParam;
import com.uwlaser.service.dto.InboundResult;
import com.uwlaser.service.mapper.DataMainMapper;
import com.uwlaser.service.mapper.DataPassStationMapper;
import com.uwlaser.service.mapper.DataProductPathMapper;
import com.uwlaser.service.mapper.ProductModelMapper;
import com.uwlaser.utils.StringUtils;
import com.uwlaser.utils.enums.DictValueEnum;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zzj
 * @date 2022年5月14日
 */
@Service
@AllArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class InboundServiceImpl implements InboundService {

    // 产品
    private final DataMainMapper dataMainMapper;
    // 产品工艺路线
    private final DataProductPathMapper dataProductPathMapper;
    // 产品过站数据
    private final DataPassStationMapper dataPassStationMapper;
    // 产品型号数据
    private final ProductModelMapper productModelMapper;

    private final DataPassStationService dataPassStationService;

    /**
     * 产品进站检测接口
     * @param requestParam
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultEntity inbound(RequestParam<InboundParam> requestParam) {

        DataMain condition = new DataMain();
        condition.setProductSn(requestParam.getParam().getProductSn());
        condition.setLienCode(requestParam.getFactoryLine().getModelCode());// 产线编码
        condition.setSectionCode(requestParam.getFactorySection().getModelCode());// 工段编码

        // 获取产品数据
        DataMain dataMain = dataMainMapper.selectOne(new QueryWrapper<>(condition));

        if (dataMain == null) {
            throw new RuntimeException("产品编码【"+requestParam.getParam().getProductSn()+"】错误！MES系统没有维护该产品编码！");
        }
        requestParam.setDataMain(dataMain);

        // 获取产品工艺路线（当前工序）
        DataProductPath dataProductPath = getProductPath(requestParam.getParam().getProductSn(), requestParam.getCraftOperation().getOperationCode());

        // 校验当前工序（是否屏蔽：屏蔽直接返回）
        if (DictValueEnum.YES.getValue().equals(dataProductPath.getIsShield())) {
            throw new RuntimeException("产品编码【"+requestParam.getParam().getProductSn()+"】错误！MES系统已屏蔽【"+dataProductPath.getOperationName()+"】工序！");
        }

        // 校验当前工序是否已经完成过了（已完成不允许重复进站，要手动修改此产品为初始化才可以）
        if (DictValueEnum.RESULT_OK.getValue().equals(dataProductPath.getResult()) || DictValueEnum.RESULT_NG.getValue().equals(dataProductPath.getResult())){
            throw new RuntimeException("产品编码【"+requestParam.getParam().getProductSn()+"】已完成【"+dataProductPath.getOperationName()+"】工序，请在MES中手动修改此产品为初始化状态再进行操作！");
        }

        // 校验当前工序（是否校验）
        if (DictValueEnum.YES.getValue().equals(dataProductPath.getIsCheck())) {

            // 校验返修（不返修就要走校验）
            if (DictValueEnum.NO.getValue().equals(dataProductPath.getRepairSign())) {

                // 如果没有上个工序代表是第一个工序不用做校验
                if (!StringUtils.isEmpty(dataProductPath.getUpStationCode())) {

                    // 获取产品工艺路线（上个工序）
                    DataProductPath upDataProductPath = getProductPath(requestParam.getParam().getProductSn(), dataProductPath.getUpStationCode());

                    // 校验上个工序是否合格，不是“合格”的产品
                    if (!DictValueEnum.RESULT_OK.getValue().equals(upDataProductPath.getResult())) {
                        // 获取上个工序状态
                        String status = "其他";
                        if (DictValueEnum.RESULT_NG.getValue().equals(upDataProductPath.getResult())) {
                            status = DictValueEnum.RESULT_NG.getLabel();
                        } else if (DictValueEnum.RESULT_UN.getValue().equals(upDataProductPath.getResult())) {
                            status = DictValueEnum.RESULT_UN.getLabel();
                        } else if (DictValueEnum.RESULT_SC.getValue().equals(upDataProductPath.getResult())) {
                            status = DictValueEnum.RESULT_SC.getLabel();
                        }

                        throw new RuntimeException("产品编码【"+requestParam.getParam().getProductSn()+"】错误！该产品上一道工序【"+upDataProductPath.getOperationName()+"】是【"+status+"】状态！");
                    }
                }
            }
        }

        // 添加进站记录
        return insertDataPassStation(requestParam);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultEntity inboundBatch(RequestParam<InboundBatchParam> requestParam) {

        // 产品SN码
        List<String> productSns = requestParam.getParam().getProductSns();

        // 获取产品数据
        List<DataMain> dataMainList = dataMainMapper.selectList(new LambdaQueryWrapper<DataMain>().in(DataMain::getProductSn, productSns));

        if (CollectionUtils.isEmpty(dataMainList)) {
            throw new RuntimeException("产品编码【"+String.join(",", productSns)+"】错误！MES系统没有维护该产品编码！");
        } else if (dataMainList.size() != productSns.size()) {

            // 移除已存在的编码，剩下的是数据库不存在的编码
            dataMainList.forEach(e -> {
                if (productSns.contains(e.getProductSn())) {
                    productSns.remove(e.getProductSn());
                }
            });
            throw new RuntimeException("产品编码【"+String.join(",", productSns)+"】错误！MES系统没有维护该产品编码！");
        }

        // 获取产品工艺路线（当前工序）
        List<DataProductPath> dataProductPathList = getProductPathList(productSns, requestParam.getCraftOperation().getOperationCode());

        // 校验当前工序（是否屏蔽：屏蔽返回提示）
        List<String> shieldList = new ArrayList<>();
        for (DataProductPath e : dataProductPathList) {
            if (DictValueEnum.YES.getValue().equals(e.getIsShield())) {// 判断是否屏蔽
                shieldList.add(e.getProductSn());// 添加已屏蔽工位的产品SN码
            }
        }

        if (!CollectionUtils.isEmpty(shieldList)) {
            throw new RuntimeException("产品编码【"+String.join(",", shieldList)+"】错误！MES系统已屏蔽【"+dataProductPathList.get(0).getOperationName()+"】工序！");
        }

        // 获取产品工艺路线（上个工序）
        List<DataProductPath> upDataProductPathList = getProductPathList(productSns, dataProductPathList.get(0).getUpStationCode());

        // List转Map（上个工序）
        Map<String, DataProductPath> upMap = upDataProductPathList.stream().collect(Collectors.toMap(DataProductPath::getProductSn, Function.identity(), (key1, key2) -> key2));

        // NG产品
        StringBuffer ngProduct = new StringBuffer();
        // 校验当前工序（是否校验）
        for (DataProductPath e : dataProductPathList) {
            if (DictValueEnum.YES.getValue().equals(e.getIsCheck())) {// 判断是否校验
                // 校验返修（返修不走校验，不返修就要走校验）
                if (DictValueEnum.NO.getValue().equals(e.getRepairSign())) {
                    // 如果没有上个工序代表是第一个工序，不用做校验
                    if (!StringUtils.isEmpty(e.getUpStationCode())) {
                        // 校验上个工序是否合格，不是“合格”的产品不能校验不通过。
                        if (!DictValueEnum.RESULT_OK.getValue().equals(upMap.get(e.getProductSn()).getResult())) {
                            // 获取上个工序状态
                            String status = "其他";
                            if (DictValueEnum.RESULT_NG.getValue().equals(upMap.get(e.getProductSn()).getResult())) {
                                status = DictValueEnum.RESULT_NG.getLabel();
                            } else if (DictValueEnum.RESULT_UN.getValue().equals(upMap.get(e.getProductSn()).getResult())) {
                                status = DictValueEnum.RESULT_UN.getLabel();
                            } else if (DictValueEnum.RESULT_SC.getValue().equals(upMap.get(e.getProductSn()).getResult())) {
                                status = DictValueEnum.RESULT_SC.getLabel();
                            }
                            ngProduct.append("【").append(e.getProductSn()).append("：").append(status).append("】");
                        }
                    }
                }
            }
        }

        // 判断是否有产品不合格
        if (ngProduct.length() > 0) {
            ngProduct.insert(0, "产品上一道工序不合格：");
            throw new RuntimeException(ngProduct.toString());
        }

        // 添加进站记录-多条
        return insertDataPassStationBatch(requestParam, dataMainList);
    }

    /**
     * 获取产品工艺路线-批量
     * @param productSns：产品SN
     * @param operationCode：工序编码
     * @return
     */
    private List<DataProductPath> getProductPathList(List<String> productSns, String operationCode) {

        if (CollectionUtils.isEmpty(productSns)) {
            throw new RuntimeException("获取产品工艺路线错误，产品条码为空！");
        } else if (StringUtils.isEmpty(operationCode)) {
//            throw new RuntimeException("获取产品工艺路线错误，工序编号为空！");
            return new ArrayList<>();
        }
        //获取工艺路线
        List<DataProductPath> dataProductPathList = dataProductPathMapper.selectList(new LambdaQueryWrapper<DataProductPath>().in(DataProductPath::getProductSn, productSns).eq(DataProductPath::getOperationCode, operationCode));

        if (CollectionUtils.isEmpty(dataProductPathList)) {
            throw new RuntimeException("当前条码：" + String.join(",", productSns) + " 工艺错误，工序编号："+operationCode+"，MES系统没有对应的工艺路线！");
        } else if (dataProductPathList.size() != productSns.size()) {

            // 移除已存在的编码，剩下的是数据库不存在的编码
            dataProductPathList.forEach(e -> {
                if (productSns.contains(e.getProductSn())) {
                    productSns.remove(e.getProductSn());
                }
            });
            throw new RuntimeException("当前条码：" + String.join(",", productSns) + " 工艺错误，工序编号："+operationCode+"，MES系统没有对应的工艺路线！");
        }
        return dataProductPathList;
    }

    /**
     * 获取产品工艺路线-单条
     * @param productSn：产品SN
     * @param operationCode：工序编码
     * @return
     */
    private DataProductPath getProductPath(String productSn, String operationCode) {
        if (StringUtils.isEmpty(productSn)) {
            throw new RuntimeException("获取产品工艺路线错误，产品条码：【" + productSn + "】为空！");
        } else if (StringUtils.isEmpty(operationCode)) {
            throw new RuntimeException("获取产品工艺路线错误，工序编号：【" + operationCode + "】为空！");
        }
        //获取工艺路线
        DataProductPath condition = new DataProductPath();
        condition.setProductSn(productSn);
        condition.setOperationCode(operationCode);

        DataProductPath dataProductPath = dataProductPathMapper.selectOne(new QueryWrapper<>(condition));
        if (dataProductPath == null) {
            throw new RuntimeException("当前条码：【" + productSn + "】工艺错误，工序编号：【"+operationCode+"】，MES系统没有对应的工艺路线！");
        }
        return dataProductPath;
    }

    /**
     * 添加进站记录-单条
     * @param rp
     * @return
     */
    private ResultEntity<InboundResult> insertDataPassStation(RequestParam<InboundParam> rp) {

        // 组装进站数据
        DataPassStation ps = new DataPassStation();
        ps.setProductSn(rp.getParam().getProductSn());
        ps.setLienCode(rp.getFactoryLine().getModelCode());
        ps.setSectionCode(rp.getFactorySection().getModelCode());
        ps.setStationCode(rp.getFactoryStation().getModelCode());
        ps.setOperationCode(rp.getCraftOperation().getOperationCode());
        ps.setDeviceCode(rp.getDeviceMain().getDeviceCode());

        // 查看是否有重复进站的，如果有重复进站的就覆盖原数据
        //由于生产过程中出现了同一秒内/间隔1秒内同时进来了两个进站请求，导致生成了两条进站记录，此处使用Limit 1做处理，防止报异常 found 2
        DataPassStation result = dataPassStationMapper.selectOne(new QueryWrapper<>(ps).last("limit 1"));
        ps.setProductCode(rp.getDataMain().getProductCode());
        ps.setProductType(rp.getDataMain().getProductType());
        ps.setOrderCode(rp.getDataMain().getOrderCode());
        ps.setWorkOrderCode(rp.getDataMain().getWorkOrderCode());
        ps.setAisleCode(rp.getParam().getAisleCode());
        ps.setTrayCode(rp.getParam().getTrayCode());
        ps.setEntryTime(new Date());
        ps.setProcessedMode(rp.getParam().getProcessedMode());
        ps.setRepairNum(0);
        ps.setCreateBy(rp.getOperator());
        ps.setShiftCode(rp.getFactoryShift().getShiftCode());
        ps.setTeamGroupId(rp.getFactoryShift().getTeamGroupId());
        if (result == null) {
            // 添加进站数据
            dataPassStationMapper.insert(ps);
        } else {
            ps.setId(result.getId());
            ps.setRepairNum(result.getRepairNum());
            // 更新进站数据
            dataPassStationMapper.updateById(ps);
        }

        // 获取产品型号（主要获取产品名称）
        ProductModel productModelCondition = new ProductModel();
        productModelCondition.setProductCode(rp.getDataMain().getProductCode());
        ProductModel productModel = productModelMapper.selectOne(new QueryWrapper<>(productModelCondition));
        if (productModel == null) {
            throw new RuntimeException("当前品型产号编码：" + rp.getDataMain().getProductCode() + " 错误，MES系统没有对应的“品型产号”数据！");
        }

        // 组装响应数据
        InboundResult ir = new InboundResult();
        ir.setProductSn(rp.getProductSn());
        ir.setProductCode(rp.getDataMain().getProductCode());
        ir.setProductType(rp.getDataMain().getProductType());
        ir.setProductName(productModel.getProductName());
        ir.setOperationCode(rp.getCraftOperation().getOperationCode());
        ir.setWorkOrderCode(rp.getDataMain().getWorkOrderCode());

        return new ResultEntity(ir);
    }

    /**
     * 添加进站记录-多条
     * @param rp
     * @param dataMainList
     * @return
     */
    private ResultEntity<InboundBatchResult> insertDataPassStationBatch(RequestParam<InboundBatchParam> rp, List<DataMain> dataMainList) {

        // 组装进站数据
        Date date = new Date();
        DataPassStation ps = null;
        List<DataPassStation> dList = new ArrayList<>();
        for (DataMain d : dataMainList) {
            ps = new DataPassStation();
            ps.setProductSn(d.getProductSn());
            ps.setProductCode(d.getProductCode());
            ps.setProductType(d.getProductType());
            ps.setOrderCode(d.getOrderCode());
            ps.setWorkOrderCode(d.getWorkOrderCode());
            ps.setLienCode(rp.getFactoryLine().getModelCode());
            ps.setSectionCode(rp.getFactorySection().getModelCode());
            ps.setStationCode(rp.getFactoryStation().getModelCode());
            ps.setOperationCode(rp.getCraftOperation().getOperationCode());
            ps.setDeviceCode(rp.getDeviceMain().getDeviceCode());
            ps.setAisleCode(rp.getParam().getAisleCode());
            ps.setTrayCode(rp.getParam().getTrayCode());
            ps.setEntryTime(date);
            ps.setProcessedMode(rp.getParam().getProcessedMode());
            ps.setRepairNum(0);
            ps.setCreateBy(rp.getOperator());
            ps.setCreateTime(date);
            ps.setShiftCode(rp.getFactoryShift().getShiftCode());
            ps.setTeamGroupId(rp.getFactoryShift().getTeamGroupId());
            dList.add(ps);
        }

        // 产品编码集合
        List<String> productSnList = dataMainList.stream().map(DataMain::getProductSn).collect(Collectors.toList());

        // 查看是否有重复进站的，如果有重复进站的就覆盖原数据
        List<DataPassStation> dataPassStationList = dataPassStationMapper.selectList(new LambdaQueryWrapper<DataPassStation>()
                .in(DataPassStation::getProductSn, productSnList)
                .eq(DataPassStation::getLienCode, rp.getFactoryLine().getModelCode())
                .eq(DataPassStation::getSectionCode, rp.getFactorySection().getModelCode())
                .eq(DataPassStation::getStationCode, rp.getFactoryStation().getModelCode())
                .eq(DataPassStation::getOperationCode, rp.getCraftOperation().getOperationCode())
                .eq(DataPassStation::getDeviceCode, rp.getDeviceMain().getDeviceCode())
        );

        if (CollectionUtils.isEmpty(dataPassStationList)) {
            // 批量添加进站数据
            dataPassStationService.saveBatch(dList);
        } else {
            for (DataPassStation newE : dList) {
                for (DataPassStation oldE : dataPassStationList) {
                    if (newE.getProductSn().equals(oldE.getProductSn()) && newE.getLienCode().equals(oldE.getLienCode())
                    && newE.getSectionCode().equals(oldE.getSectionCode()) && newE.getStationCode().equals(oldE.getStationCode())
                    && newE.getOperationCode().equals(oldE.getOperationCode()) && newE.getDeviceCode().equals(oldE.getDeviceCode())) {
                        newE.setId(oldE.getId());// 设置ID
                        newE.setRepairNum(oldE.getRepairNum());

                    }
                }
            }

            // 按ID过滤。
            List<DataPassStation> addList = dList.stream().filter(e -> e.getId() == null).collect(Collectors.toList());
            // 按ID过滤。
            List<DataPassStation> updateList = dList.stream().filter(e -> e.getId() != null).collect(Collectors.toList());

            // 批量添加进站数据
            if (!CollectionUtils.isEmpty(addList)) {
                dataPassStationService.saveBatch(addList);
            }
            // 批量更新进站数据
            if (!CollectionUtils.isEmpty(updateList)) {
                dataPassStationService.updateBatchById(updateList);
            }
        }

        // 获取产品型号（主要获取产品名称）
        ProductModel productModelCondition = new ProductModel();
        productModelCondition.setProductCode(dataMainList.get(0).getProductCode());
        ProductModel productModel = productModelMapper.selectOne(new QueryWrapper<>(productModelCondition));
        if (productModel == null) {
            throw new RuntimeException("当前品型产号编码：【" + dataMainList.get(0).getProductCode() + "】错误，MES系统没有对应的“品型产号”数据！");
        }

        // 组装响应数据
        InboundBatchResult ir = new InboundBatchResult();
        dataMainList.forEach(e -> ir.addProductSn(e.getProductSn(), "", 200));// 产品SN
        ir.setProductCode(dataMainList.get(0).getProductCode());
        ir.setProductType(dataMainList.get(0).getProductType());
        ir.setProductName(productModel.getProductName());
        ir.setOperationCode(rp.getCraftOperation().getOperationCode());
        ir.setWorkOrderCode(dataMainList.get(0).getWorkOrderCode());
        return new ResultEntity(ir);
    }
}

